/*
 * Description: Hash table implementation.
 *     History: yang@haipo.me, 2016/03/16, create
 */

# pragma once

# include <stdint.h>
# include <stddef.h>
# include <malloc.h>
# include <string.h>
#include <mpdecimal.h>
#include "ut_sds.h"
#include "ut_skiplist.h"

typedef struct dict_entry {
    void *key;
    void *val;
    uint64_t id;
    struct dict_entry *next;
} dict_entry;

typedef struct dict_types {
    uint32_t (*hash_function)(const void *key);
    void *(*key_dup)(const void *key);
    void *(*val_dup)(const void *val);
    int (*key_compare)(const void *key1, const void *key2);
    void (*key_destructor)(void *key);
    void (*val_destructor)(void *val);
} dict_types;

typedef struct dict_t {
    dict_entry **table;
    dict_types type;
    uint32_t size;
    uint32_t mask;
    uint32_t used;
    uint64_t id_start;
    uint64_t id_clear;
} dict_t;

typedef struct dict_iterator {
    dict_t *dt;
    int64_t index;
    dict_entry *entry;
    dict_entry *next_entry;
} dict_iterator;

# define dict_size(dt) (dt)->used
# define dict_slot(dt) (dt)->size

uint32_t dict_generic_hash_function(const void *data, size_t len);

dict_t *dict_create(dict_types *type, uint32_t init_size);
dict_entry *dict_add(dict_t *dt, const void *key, void *val);
dict_entry *dict_find(dict_t *dt, const void *key);
int dict_expand(dict_t *dt, uint32_t size);
int dict_replace(dict_t *dt, void *key, void *val);
int dict_delete(dict_t *dt, const void *key);
void dict_release(dict_t *dt);

void dict_clear(dict_t *dt);
void dict_mark_clear(dict_t *dt);

dict_iterator *dict_get_iterator(dict_t *dt);
dict_entry *dict_next(dict_iterator *iter);
void dict_release_iterator(dict_iterator *iter);

// ---- plus start ----
struct dict_key_base {
    virtual uint32_t hash() const { return 0;}
    virtual dict_key_base* dup() { return NULL;}
    virtual int compare(const dict_key_base *key2) const { return 0;}
    virtual void release() {}
};

struct dict_value_base {
    virtual dict_value_base* dup() { return NULL;}
    virtual void release() {}
};

inline uint32_t dict_plus_hash_function(const void *key) {
    const dict_key_base* k = (dict_key_base*)(key);
    return k->hash();
}
inline void *dict_plus_key_dup(const void *key) {
    dict_key_base* k = (dict_key_base*)(key);
    return k->dup();
}
inline void *dict_plus_val_dup(const void *val) {
    dict_value_base* v = (dict_value_base*)(val);
    return v->dup();
}
inline int dict_plus_key_compare(const void *key1, const void *key2) {
    const dict_key_base* k1 = (dict_key_base*)(key1);
    const dict_key_base* k2 = (dict_key_base*)(key2);
    return k1->compare(k2);
}
inline void dict_plus_key_destructor(void *key) {
    dict_key_base* k = (dict_key_base*)(key);
    k->release();
}
inline void dict_plus_val_destructor(void *val) {
    dict_value_base* v = (dict_value_base*)(val);
    v->release();
}

template <typename TKeyPointer, typename TValuePointer>
struct dict_entry_plus : public dict_entry {
    TKeyPointer get_key() {
        return static_cast<TKeyPointer>(this->key);
    }
    TValuePointer get_value() {
        return static_cast<TValuePointer>(this->val);
    }
};

template <typename TKeyPointer, typename TValuePointer>
struct dict_iterator_plus : public dict_iterator {
    dict_entry_plus<TKeyPointer, TValuePointer>* next() {
        return static_cast<dict_entry_plus<TKeyPointer, TValuePointer>*>(dict_next(this));
    }
    void release() {
        dict_release_iterator(this);
    }
};

template <typename TKeyPointer, typename TValuePointer>
struct dict_plus : public dict_t {
    static dict_plus<TKeyPointer,TValuePointer>* create(uint32_t init_size=64, bool ref_val = true) {
        dict_types dt {
                .hash_function  = dict_plus_hash_function,
                .key_dup        = dict_plus_key_dup,
                .val_dup        = ref_val ? dict_plus_val_dup : NULL,
                .key_compare    = dict_plus_key_compare,
                .key_destructor = dict_plus_key_destructor,
                .val_destructor = ref_val ? dict_plus_val_destructor : NULL
        };
        return static_cast<dict_plus<TKeyPointer,TValuePointer>*>(dict_create(&dt,init_size));
    }
    void release() {
        dict_release(this);
    }
    dict_entry_plus<TKeyPointer, TValuePointer>* add(TKeyPointer key, TValuePointer val) {
        return static_cast<dict_entry_plus<TKeyPointer, TValuePointer>*>(dict_add(this, key, val));
    }
    dict_entry_plus<TKeyPointer, TValuePointer>* find(const TKeyPointer key) {
        return static_cast<dict_entry_plus<TKeyPointer, TValuePointer>*>(dict_find(this, key));
    }
    int expand(uint32_t size) {
        return dict_expand(this, size);
    }
    int replace(TKeyPointer key, TValuePointer val) {
        return dict_replace(this,key,val);
    }
    int remove(const TKeyPointer key) {
        return dict_delete(this, key);
    }
    void clear() {
        dict_clear(this);
    }
    void mark_clear() {
        dict_mark_clear(this);
    }
    dict_iterator_plus<TKeyPointer, TValuePointer>* get_iterator() {
        return static_cast<dict_iterator_plus<TKeyPointer, TValuePointer>*>(dict_get_iterator(this));
    }
    uint32_t get_size() const{
        return this->used;
    }
};

template <typename TKey>
struct mem_dict_key : public dict_key_base{
    virtual uint32_t hash() const override{
        return dict_generic_hash_function(this, sizeof(TKey));
    }
    virtual dict_key_base* dup() override{
        TKey *obj = (TKey*)malloc(sizeof(TKey));
        memcpy(obj, this, sizeof(TKey));
        return obj;
    }
    virtual int compare(const dict_key_base *key2) const override{
        return memcmp(this, key2, sizeof(TKey));
    }
    virtual void release() override{
        free(this);
    }
};

template <typename TValue>
struct mem_dict_value : public dict_value_base{
    virtual dict_value_base* dup() override{
        TValue *obj = (TValue*)malloc(sizeof(TValue));
        memcpy(obj, this, sizeof(TValue));
        return obj;
    }
    virtual void release() override{
        free(this);
    }
};

struct nocopy_dict_value : public dict_value_base{
    virtual dict_value_base* dup() override {
        return this;
    }
};

inline uint32_t dict_plus_str_hash_function(const void *key) {
    return dict_generic_hash_function(key, strlen((const char*)key));
}
inline void *dict_plus_str_key_dup(const void *key) {
    return strdup((const char*)key);
}
inline int dict_plus_str_key_compare(const void *key1, const void *key2) {
    return strcmp((const char*)key1, (const char*)key2);
}
inline void dict_plus_str_key_destructor(void *key) {
    free(key);
}

template <typename TValuePointer>
static dict_plus<const char*, TValuePointer>* create(uint32_t init_size=64, bool ref_val = true) {
    dict_types dt {
            .hash_function  = dict_plus_str_hash_function,
            .key_dup        = dict_plus_str_key_dup,
            .val_dup        = ref_val ? dict_plus_val_dup : NULL,
            .key_compare    = dict_plus_str_key_compare,
            .key_destructor = dict_plus_str_key_destructor,
            .val_destructor = ref_val ? dict_plus_val_destructor : NULL
    };
    return static_cast<dict_plus<const char,TValuePointer>*>(dict_create(&dt,init_size));
}

inline uint32_t dict_plus_sds_hash_function(const void *key) {
    return dict_generic_hash_function(key, sdslen((sds)key));
}
inline void *dict_plus_sds_key_dup(const void *key) {
    return sdsdup((sds)key);
}
inline int dict_plus_sds_key_compare(const void *key1, const void *key2) {
    return sdscmp((sds)key1, (sds)key2);
}
inline void dict_plus_sds_key_destructor(void *key) {
    sdsfree((sds)key);
}

template <typename TValuePointer>
static dict_plus<sds, TValuePointer>* create(uint32_t init_size=64, bool ref_val = true) {
    dict_types dt {
            .hash_function  = dict_plus_sds_hash_function,
            .key_dup        = dict_plus_sds_key_dup,
            .val_dup        = ref_val ? dict_plus_val_dup : NULL,
            .key_compare    = dict_plus_sds_key_compare,
            .key_destructor = dict_plus_sds_key_destructor,
            .val_destructor = ref_val ? dict_plus_val_destructor : NULL
    };
    return static_cast<dict_plus<sds,TValuePointer>*>(dict_create(&dt,init_size));
}

inline void *dict_plus_mpd_t_val_dup(const void *val) {
    return mpd_qncopy((mpd_t*)val);
}
inline void dict_plus_mpd_t_val_destructor(void *val) {
    mpd_del((mpd_t*)val);
}

template <typename TKeyPointer>
static dict_plus<TKeyPointer, mpd_t*>* create(uint32_t init_size=64, bool ref_val = true) {
    dict_types dt {
            .hash_function  = dict_plus_hash_function,
            .key_dup        = dict_plus_key_dup,
            .val_dup        = ref_val ? dict_plus_mpd_t_val_dup : NULL,
            .key_compare    = dict_plus_key_compare,
            .key_destructor = dict_plus_key_destructor,
            .val_destructor = ref_val ? dict_plus_mpd_t_val_destructor : NULL
    };
    return static_cast<dict_plus<TKeyPointer, mpd_t*>*>(dict_create(&dt,init_size));
}

inline void dict_plus_skiplist_val_destructor(void *val) {
    skiplist_release((skiplist_t*)val);
}

template <typename TKeyPointer>
static dict_plus<TKeyPointer, skiplist_t*>* create(uint32_t init_size=64, bool ref_val = true) {
    dict_types dt {
            .hash_function  = dict_plus_hash_function,
            .key_dup        = dict_plus_key_dup,
            .val_dup        = NULL,
            .key_compare    = dict_plus_key_compare,
            .key_destructor = dict_plus_key_destructor,
            .val_destructor = dict_plus_skiplist_val_destructor
    };
    return static_cast<dict_plus<TKeyPointer, mpd_t*>*>(dict_create(&dt,init_size));
}

// ---- plus end ----